#pragma once
#include "base/core/lua_head.h"
#include "base/core/Profiler.h"

using   luabridge::LuaRef;
typedef lua_Alloc  LuaAlloc;

class LuaScript 
{
public:
     LuaScript(lua_State* state = nullptr);
     virtual ~LuaScript();

public:
    int  Init(bool auto_gc = false, LuaAlloc alloc = NULL);
    void UnInit();
    void Reset();

    // -1 garbage all, usually 1 or 2
    bool  GarbageCollect(int step = -1);
  
    bool  LoadFile(const char* path);
    bool  LoadScriptString(const char* buffer);
    lua_State* GetState();

    bool  LoadScriptFromDataFile(const char* path);
    bool  LoadScriptFromData(const char *buff, size_t sz);

public:
    bool        IsNil(int index);
    bool        IsBool(int index);
    bool        IsLUser(int index);
    bool        IsNumber(int index);
    bool        IsString(int index);
    bool        IsTable(int index);
    bool        IsFunction(int index);
    bool        IsUserData(int index);
    bool        IsThread(int index);

    int         GetInt(int index);
    bool        GetBool(int index);
    double      GetNumber(int index);
    const char* GetStr(int index);
    const char* GetLStr(int index, size_t& nLen);
    void*       GetObj(int index);
    int         GetObjLen(int index);
    int         GetTopIndex();

    bool        PushInt(int value);
    bool        PushNumber(double value);
    bool        PushBool(bool value);
    bool        PushString(const char* str);
    bool        PushLString(const char* str, size_t len);
    bool        PushStringFormat(const char* format, ...);
    template <class T>
    bool        PushObj(T t);
    bool        PushNil();
    bool        SetTopIndex(int index);
    bool        PushTable();
    bool        SetTableIndex(int index);
    bool        SetTableField(const char* name);

    template <class T>
    bool        SetGlobal(const char* name, T t);
    LuaRef      GetGlobal(const char* name);
    int         GetGlobalFun(const char* str);
    static int  GetGlobalFun(lua_State* state, const char* str);
    int         SaveValue2Buffer(unsigned char* buf, int bufSize, int index);
    bool        LoadBuffer2Value(const unsigned char* buf, int bufSize);

public:
    template<typename... PARAM>
    bool  CallFuncVoid(const char* func, PARAM... params);

    template<typename RETURN, typename... PARAM>
    bool  CallFuncRet(const char * func, RETURN & retValue, PARAM... params);

    template<typename... PARAM>
    bool  CallTableFuncVoid(const char* table, const char* func, PARAM... params);

    template<typename RETURN, typename... PARAM>
    bool  CallTableFuncRet(const char* table, const char * func, RETURN & retValue, PARAM... params);
  
    bool  CallTableFuncVoidFromStack(const char* table, const char * func, int beginStackIndex, int paramCount);

    template<typename RETURN>
    bool  CallTableFuncRetFromStack(const char* table, const char * func, RETURN & retValue, int beginStackIndex, int paramCount);

    bool  CallTableFuncMultiRetFromStack(const char* table, const char * func, int retCount, int beginStackIndex, int paramCount);

protected:
    bool InitLuaState();
    void OutputError(const char* func, luabridge::LuaException& err);
    void OutputError(const char* table, const char* func, luabridge::LuaException& err);
    bool CheckRet(int ret);

    // ��ջ����һ��ֵת���ɿ��Դ��桢���͵�Buffer������ջ
    int _SaveValue2Buffer(unsigned char* buf, int bufSize);

    // ��ջ����tableת����Buffer����_SaveValue2Buffer���ã�����ջ��
    int _SaveTable2Buffer(unsigned char* buf, int bufSize);

    // ��Bufferת����һ��ֵ������ջ��
    // ����ʹ���˵�Buffer���ȣ�0��ʾ��������
    int _LoadBuffer2Value(const unsigned char* buf, int bufSize);

    // ��Buffer����'{'��ת����һ��table������ջ������_LoadBuffer2Value���ã�
    // ����ʹ���˵�Buffer���ȣ�����'}'����0��ʾ��������
    int _LoadBuffer2Table(const unsigned char* buf, int bufSize);

protected:
	bool       is_auto_gc;
	LuaAlloc   lua_alloc;
	lua_State* lua_state_;
};

// should call EndProfiler();
void BeginScriptProfilerTimer(const char *table, const char *func);
void EndScriptProfilerTimer();

#include "base/core/script.hpp"
